from __future__ import absolute_import
import os
import sys
import time
import serial
import struct
import six
from pinpong.board import I2C
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), './')))
from src.L76K import DFRobot_GNSS
from src.SD3031 import DFRobot_SD3031

class DFRobot_GNSSAndRTC(DFRobot_GNSS, DFRobot_SD3031):
    MODULE_I2C_ADDRESS = 0x66  # < Sensor device address

    # Custom function registers 0x24 ~ 0x2F
    REG_CALIB_STATUS_REG = 0x2A
    REG_CALIB_RTC_REG = 0x2B

    REG_RTC_READ_REG = 0x2E
    REG_RTC_READ_LEN = 0x2F

    # Custom function registers 0xAA ~ (0xB0 - 1)
    REG_CS32_PID = 0xAA
    REG_CS32_VID = 0xAC
    REG_CS32_VERSION = 0xAE

    MODULE_DFR1103_PID = 0x444F  # Sensor PID
    MODULE_DFR1103_VID = 0x3343  # < Sensor VID
    MODULE_DFR1103_VERSION = 0x0100  # < Sensor VERSION

    # RTC 模块校准状态
    ECALIB_NONE = 0x00
    ECALIB_COMPLETE = 0x01
    EUNDER_CALIB = 0x02

    def begin(self):
        '''!
          @brief subclass initialization function
          @return bool type, means returning initialization status
          @retval true NO_ERROR
        '''
        data = [0x80]
        self._write_reg(DFRobot_SD3031.SD3031_REG_I2C_CON, data, 1)
        return True

    def calib_rtc(self):
        '''!
          @brief Calibrate RTC immediately with GNSS
          @note This is a single calibration;
          @n If the GNSS module signal is weak, time calibration may encounter issues.
        '''
        mode = [self.EUNDER_CALIB]
        self._write_reg(self.REG_CALIB_STATUS_REG, mode, 1)

    def calib_rtc_hour(self, hour):
        '''!
          @brief The loop automatically performs GNSS timing based on the set interval
          @param hour Automatic calibration of the time interval. range: 0~255, unit: hour.
          @note When set to zero, automatic time calibration is disabled.
          @n Enabling it will trigger an immediate calibration.
          @n If the GNSS module signal is weak, time calibration may encounter issues.
        '''
        self._write_reg(self.REG_CALIB_RTC_REG, [hour], 1)

    def calib_status(self, mode=True):
        '''!
          @brief Current clock calibration status
          @param mode By default, it is set to true, indicating access to the calibration status only.
          @n  If continuous calibration for one minute does not return a successful calibration,
          @n  you can pass in false to manually terminate this calibration session.
          @return uint8_t type, indicates current clock calibration status
          @retval 0 Not calibrated
          @retval 1 Calibration complete
          @retval 2 Under calibration
          @note Note: To avoid affecting subsequent calibration status,
          @n    "Calibration completed Status (1)" is automatically zeroed after a successful read
        '''
        status = [self.ECALIB_NONE]
        if mode:
            self._read_reg(self.REG_CALIB_STATUS_REG, status, 1)
        else:
            self._write_reg(self.REG_CALIB_STATUS_REG, status, 1)
        return status[0] & 0xff

class DFRobot_GNSSAndRTC_I2C(DFRobot_GNSSAndRTC):
    def __init__(self, i2c_bus=0, addr=DFRobot_GNSSAndRTC.MODULE_I2C_ADDRESS):
        super(DFRobot_GNSSAndRTC_I2C, self).__init__()
        self.i2c_uart_flag = DFRobot_GNSSAndRTC.GNSS_I2C_FLAG
        self.__i2c_bus = I2C(i2c_bus)
        self.__device_addr = addr

    def begin(self):
        if not self.scan():
            return False
        return super(DFRobot_GNSSAndRTC_I2C, self).begin()

    def _write_reg(self, reg, p_buf, size):
        if not p_buf:
            print("p_buf ERROR!")
            return 1
        buf = p_buf[:size]
        try:
            self.__i2c_bus.writeto_mem(self.__device_addr, reg, buf)
            time.sleep(0.05)
            return 0
        except KeyboardInterrupt:
            raise
        except:
            print("Write: I2C communication failed, please check the peripherals.!")
            time.sleep(0.05)
            return 1

    def _read_reg(self, reg, p_buf, size):
        if not p_buf:
            print("p_buf ERROR!")
            return 1
        try:
            for i in range(size):
                p_buf[i] = self.__i2c_bus.readfrom_mem_restart_transmission(self.__device_addr, reg + i, 1)[0]
            return 0
        except KeyboardInterrupt:
            raise
        except:
            print("Read: I2C communication failed, please check the peripherals.!")
            return 1

    def scan(self):
        try:
            self.__i2c_bus.readfrom(self.__device_addr, 1)
            return True
        except KeyboardInterrupt:
            raise
        except:
            return False
